/*
 * Creep - manage the AI and display the enemies
 *
 * Copyright 2007 James W. Hofmann
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#if flash9
import flash.display.BitmapData;
import flash.geom.Point;
import flash.Lib;
import flash3f.Images;
#end
import lib3f.IntPoint;
import lib3f.LCG;

class Creep
{
	public var creeptype : Int;
	public var tx : Int;
	public var ty : Int;
	public var pixelsadvanced : Int;
	#if flash9
	public var sprite : CreepSprite;
	#end
	public var speed : Int;
	public var myentrypoint : Int;
	public var health : Int;
	public var strategy : Dynamic;
	
	//public static var allcreeps : Array<Creep>;
	//public static var entrypoints : Array<IntPoint>;
	//public static var shore : Array<Array<GameTile>>;
	//public static var preferredshore : Array<Array<GameTile>>;
	
	public static function addentry(p : IntPoint)
	{
			Game.cur.creepman.entrypoints.push(p);
			#if flash9
			var nb : IsoBitmap = new IsoBitmap(Images.getdata(0xEEEE00));
			IsoManager.add(nb);
			nb.offy=-13;
			nb.offx=0;
			nb.movep(IsoMetrics.tiletopix_point(p));
			#end
	}
		
	public function new(ncreeptype : Int, nx : Int, ny : Int, entry : Int)
	{
		creeptype = ncreeptype;		
		pixelsadvanced = 0;
		tx = nx;
		ty = ny;		
		
		Game.cur.map[tx][ty].creeps.push(this);
		
		myentrypoint = entry;
		
		switch (creeptype)
		{
			default: 
			{
				#if flash9
				sprite = new CreepSprite(0xEEEE06,
										 0xEEEE0A,
										 0xEEEE0E,
										 0xEEEE02, 
										 3, 3);
				#end
				speed = 1;
				switch(Game.cur.gamemode)
				{
					case(0): // normal
						health = 3 + (Game.cur.fight.wave-1);
					case(1): // hard
						health = 3 + 2*(Game.cur.fight.wave-1);
				}
				strategy = new AntStrategy(this);

			}		
			case(1): 
			{
				#if flash9
				sprite = new CreepSprite(0xEEEE12,
										 0xEEEE13,
										 0xEEEE14,
										 0xEEEE11, 
										 1, 1);
				#end
				speed = 1;
				health = 3 + (Game.cur.fight.wave-1);
				strategy = new TransportStrategy(this);
				
			}
			case(2): 
			{
				#if flash9
				sprite = new CreepSprite(0xEEEE26,
										 0xEEEE2A,
										 0xEEEE2E,
										 0xEEEE22, 
										 3, 3);
				#end
				speed = 1;
				health = 3 + (Game.cur.fight.wave-1);
				strategy = new RedAntStrategy(this);
				
			}

		}
		
		#if flash9
		IsoManager.add(sprite);		
		sprite.movep(IsoMetrics.tiletopix_xy(tx,ty));		
		#end
		Game.cur.creepman.allcreeps.push(this);
		
		strategy.strategy();
		
	}
		
	public function move()
	{
	
		// pixelsadvanced is centered at the center of the tile		
		pixelsadvanced+=speed;
		
		if (pixelsadvanced>6)
		{
			Game.cur.map[tx][ty].creeps.remove(this);
			tx += strategy.ntx;
			ty += strategy.nty;
			Game.cur.map[tx][ty].creeps.push(this);
			pixelsadvanced-=12;			
		}
		else if (pixelsadvanced==0) // || (Game.cur.map[tx+ntx][ty+nty].fort!=null))
		{
			strategy.strategy();		
		}

		var ntx : Int = strategy.ntx;
		var nty : Int = strategy.nty;
		
		#if flash9
		sprite.movep(IsoMetrics.tiletopix_xy_fine(tx,ty,
												  pixelsadvanced*ntx,
												  pixelsadvanced*nty));
												  
		if (ntx<0) //ul
		{
			sprite.update(0);
		}
		else if (nty>0) //ur
		{
			sprite.update(1);
		}
		else if (ntx>0) //dl
		{
			sprite.update(2);
		}		
		else if (nty<0) //dr
		{
			sprite.update(3);
		}
		#end
		
	}
	
	public function kill()
	{
	
		#if flash9
		IsoManager.remove(sprite);
		sprite.bitmapData.dispose();
		sprite = null;
		SFX.creepdeath = true;
		#end
		Game.cur.map[tx][ty].creeps.remove(this);
		Game.cur.creepman.allcreeps.remove(this);
		Game.cur.fight.killsthisround++;
	}	

	public static function hurtpoint(cx,cy, damage) : Int
	{
	
		var hurtthese : Array <Creep> = new Array();
		
		for (creep in Game.cur.map[cx][cy].creeps)
		{
			hurtthese.push(creep);
		}
		if (cx>0) {
		for (creep in Game.cur.map[cx-1][cy].creeps)
		{
			if (creep.strategy.ntx==1)
			hurtthese.push(creep);
		}
		}
		if (cx<25) {
		for (creep in Game.cur.map[cx+1][cy].creeps)
		{
			if (creep.strategy.ntx==-1)
			hurtthese.push(creep);
		}
		}
		if (cy>0) {
		for (creep in Game.cur.map[cx][cy-1].creeps)
		{
			if (creep.strategy.nty==1)
			hurtthese.push(creep);
		}
		}
		if (cy<25) {
		for (creep in Game.cur.map[cx][cy+1].creeps)
		{
			if (creep.strategy.nty==-1)
			hurtthese.push(creep);
		}
		}		
		
		for (creeptohurt in hurtthese)
		{
			creeptohurt.health-=damage;
		
			if (creeptohurt.health<=0)
				{
				creeptohurt.kill();
				}
		}

		return hurtthese.length;
		
	}
	
}

#if flash9
class CreepSprite extends IsoBitmap
{
	public var ulani : Array<BitmapData>;
	public var urani : Array<BitmapData>;
	public var dlani : Array<BitmapData>;
	public var drani : Array<BitmapData>;
	
	public var cycle : Int;
	public var delay : Int;
	public var maxdelay : Int;
	
	public function new(ul : Int,  
						ur : Int, 
						dl : Int, 
						dr : Int, anilength : Int,
						anidelay : Int)
	{
		super(Images.getdata(ul));
				
		offx = 2;
		offy = 0;
		cycle = 0;
		delay = 0;
		maxdelay = anidelay;
		
		ulani = new Array();
		urani = new Array();
		dlani = new Array();
		drani = new Array();
		
		for (n in 0...anilength)
		{
			ulani.push(Images.getdata(ul+n));
			urani.push(Images.getdata(ur+n));
			dlani.push(Images.getdata(dl+n));
			drani.push(Images.getdata(dr+n));
		}
		
	}
	
	public function update(dir : Int)
	{
		switch(dir)
		{
			case(0):
			this.bitmapData = ulani[cycle];
			case(1):
			this.bitmapData = urani[cycle];
			case(2):
			this.bitmapData = dlani[cycle];
			case(3):
			this.bitmapData = drani[cycle];
		}
	
		delay+=1;
		
		if (delay==maxdelay)
		{
			cycle+=1;
			delay = 0;
		}
		if (cycle==ulani.length)
		{
			cycle = 0;
		}
	}
	
}
#end

class AntStrategy
{

	public var wanderx : Int;
	public var wandery : Int;
	public var wandercount : Int;

	public var ntx : Int;
	public var nty : Int;
	public var parent : Creep;

	public var preferredpath : Int; // -1: figure out what to do next/wander; 0-2: castle paths; -2: step off boat
	public var wanttowanderpercent : Int;
	
	public function new(parent : Creep)
	{
		wanderx = 0;
		wandery = 0;
		wandercount = 5;
		ntx = 0;
		nty = 0;
		this.parent = parent;
		preferredpath = -2;
		wanttowanderpercent = LCG.randInt(10)+80;
	}

	public function strategy()
	{
	
		ntx = 0;
		nty = 0;		

		if (Game.cur.map[parent.tx][parent.ty].terrain==-1)
		{
			offtheboat();
		}
		else if (LCG.randInt(100)>wanttowanderpercent)
		{
			if (preferredpath>=0) // follow an existing path
			{
				var patht : GameTile = Game.cur.map[parent.tx][parent.ty].paths[preferredpath];
				if (patht!=null)
				{
					ntx = -parent.tx + patht.x;
					nty = -parent.ty + patht.y;
				}
				else
				{
					preferredpath = -1;
				}
			}
			else
			{
				var okpaths : Array<Int> = new Array();
				
				for (p in 0...3)
				{
					var cp : GameTile = Game.cur.map[parent.tx][parent.ty].paths[p];
					if (cp!=null)
					{
						if (MapGen.castles[p].fort!=null)
						{
							okpaths.push(p);
						}
					}
				}

				if (okpaths.length>0)
				{
					preferredpath = okpaths[LCG.randInt(okpaths.length-1)];
				}
				else
				{
					preferredpath = -1;
					if (LCG.randInt(20)>1)
						wander();
				}
			}
		}
		else
		{
			if (LCG.randInt(20)>1)
				wander();
		}

		if (FortressManager.nearfort(parent.tx, parent.ty))
		{
			for (n in Game.cur.map[parent.tx][parent.ty].links)
			{
				if (n.fort!=null)
				{
					ntx = n.x-parent.tx;
					nty = n.y-parent.ty;			
				}
			}
			FortressManager.hurt(parent.tx+ntx, parent.ty+nty, 1);
			parent.speed=0;
		}
		else
		{
			parent.speed=1;
		}
		
	}
	
	public function offtheboat()
	{

		var oklinks : Array<GameTile> = new Array();
	
		for (link in 0...Game.cur.map[parent.tx][parent.ty].links.length)
		{
			
			var ok = false;
			
				for (p in 0...3)
				{
					var cp : GameTile = Game.cur.map[parent.tx][parent.ty].paths[p];
					if (cp!=null)
					{
						if (MapGen.castles[p].fort!=null)
						{
							ok = true;
							break;
						}
					}
				}

			if (ok)
				oklinks.push(Game.cur.map[parent.tx][parent.ty].links[link]);
				
		}
	
		if (oklinks.length>0)
		{
			var nextnum = LCG.randInt(oklinks.length-1);
			var next = oklinks[nextnum];
			ntx = next.x - parent.tx;
			nty = next.y - parent.ty;
			preferredpath = -1;
		}
		else
		{
			wander();
		}
		
	}

	public function wander()
	{
	
		var valid : Array<GameTile> = new Array();
		var next : GameTile;
		
		var tx : Int = parent.tx;
		var ty : Int = parent.ty;
		
		wandercount++;
		
		if (wandercount>3)
		{
			wandercount=0;
			if (LCG.randInt(1)==1)
			{
				wanderx = LCG.randInt(2)-1;
				wandery = 0;
			}
			else
			{
				wanderx = 0;
				wandery = LCG.randInt(2)-1;
			}
		}

		var lastresort : GameTile = null;
		
		for (n in Game.cur.map[tx][ty].links)
		{
		
			if (FortressManager.test(n.x, n.y))
			{
				var ok = false;
				for (z in n.links)
				{
					if (FortressManager.test_land(z.x,z.y))
						ok = true;
				}
				if (ok)
					valid.push(n);
				else
					lastresort = n;
			}		
		}
		
		if (valid.length>0)
		{
			for (n in valid)
			{
				if ((n.x-tx)==wanderx && (n.y-ty)==wandery)
					next = n;
			}
			if (next==null)
			{
				next = valid[LCG.randInt(valid.length-1)];
				wanderx = wandery;
				wandery = -wanderx;
			}
		}
		else if (lastresort!=null) // we have been told to march onto an island with no exits
		{
			next = lastresort;
		}
		else if (FortressManager.test(tx,ty)) // if we're stuck on a 1-square island, stop
		{
			next = Game.cur.map[tx][ty];
		}
		else // we're in the ocean and look ridiculous so get moving off it already
		{
			next = Game.cur.map[tx][ty].links[LCG.randInt(Game.cur.map[tx][ty].links.length-1)];
		}
		ntx = next.x - tx;
		nty = next.y - ty;
		
	}

}

class RedAntStrategy extends AntStrategy
{

	public function new(parent : Creep)
	{
		super(parent);
		wanttowanderpercent-=80;
	}
	
}

class TransportStrategy
{

	//public static var deploycount : Int = 3;
	public var deploydelay : Int;
	
	public var ntx : Int;
	public var nty : Int;
	public var parent : Creep;
	public var route : Array<IntPoint>;
	public var reverse : Bool;
	public var release : Bool;
	public var releasecount : Int;
	public var releaseinterval : Int;
	
	public function new(parent : Creep)
	{
		ntx = 0;
		nty = 0;
		this.parent = parent;
		reverse = false;
		release = false;
		releaseinterval=0;
		releasecount=0;
		pickshore();
	}

	public function strategy()
	{

		ntx = 0;
		nty = 0;

		var tx : Int = parent.tx;
		var ty : Int = parent.ty;

		if (reverse)
			followreverse();
		else
			followpath();
		
		
		if (release)
		{
		
			if (releaseinterval>=5)
			{
				if (LCG.randInt(1)==0)
				{
					new Creep(2,parent.tx, parent.ty,parent.myentrypoint);
				}
				else
				{
					new Creep(0,parent.tx, parent.ty,parent.myentrypoint);
				}
				releaseinterval=0;
				releasecount++;
			}
			else
			{
				releaseinterval++;
			}
			
			if (releasecount>=3 && releaseinterval>=4)
			{
				reverse = true;
				release = false;
				releasecount = 0;
				releaseinterval = 0;
			}
		}
				
	}

	public function pickshore()
	{
	
		// choose the point to land from
		var entry : Int = parent.myentrypoint;
		var targetset : Array<GameTile>;
		
		// below: if we see shores that have forts on them, give them priority
		
		if (Game.cur.creepman.preferredshore[entry].length>0)
			targetset = Game.cur.creepman.preferredshore[entry];
		else
			targetset = Game.cur.creepman.shore[entry];		
		var choice : GameTile = targetset[LCG.randInt(targetset.length-1)];
		
		// make route
		route = new Array();
		var cur : GameTile = choice;
				
		while (cur.waterpaths[entry]!=null)
		{
			route.push(new IntPoint(cur.x-cur.waterpaths[entry].x,cur.y-cur.waterpaths[entry].y));
			cur = cur.waterpaths[entry];
		}
		
	}

	public function followpath()
	{
		if (route.length==0)
		{
			parent.speed = 0;
			ntx = 0;
			nty = 0;
			release = true;
		}
		else
		{
				var cur : IntPoint = route.pop();
				ntx = cur.x;
				nty = cur.y;
		}
	}
	
	public function followreverse()
	{
	
			var path : GameTile = Game.cur.map[parent.tx][parent.ty].waterpaths[parent.myentrypoint];
			if (Game.cur.creepman.entrypoints[parent.myentrypoint].x!=parent.tx ||
				Game.cur.creepman.entrypoints[parent.myentrypoint].y!=parent.ty)
			{

				if (path==null)
				{
					trace(["fail ",parent.tx," ",parent.ty]);			
					trace([Game.cur.map[parent.tx][parent.ty].waterpaths[0]]);
					trace([Game.cur.map[parent.tx][parent.ty].waterpaths[1]]);
					trace([Game.cur.map[parent.tx][parent.ty].waterpaths[2]]);
				}
			
				else
				{
					ntx = path.x - parent.tx;
					nty = path.y - parent.ty;
					reverse=true;
					parent.speed = 1;
				}
			}
			else
			{
				pickshore();
				reverse = false;
			}
	
	}
	
	// introduce a new type of creep, the builder
	// a graph to calculate risk of various areas on the map:
	// simply, each wall, tower, castle adds a bit of influence
	// (this can be done by reusing territory again?) nah, make a new var
	// invasion transports will land where there is low risk and paths to forts
	// the remainder land in pure lowest risk
	
	// builders will build enemy camps
	// camps spawn fliers
	
	// change tower placement rules to 2 square diamond around existing areas
	// this allows for better defense/expansion into outlying areas
	
	// we need fast transports
	
}
